﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Swift;
using Swift.Lib;

namespace CB.Logic
{
    public enum GameMode
    {
        Color,
        Image,
    }

    public interface IGameData
    {
        IGameData Init(int w, int h, int c, int srandSeed);
        bool GameFinish { get; }
        bool BlockMatch(int x, int y);        
        void RandomMove(SRandom srand, int times);
        List<Direction> DirectionsCanMove();
        bool DoMove(Direction direction, bool playerOp, out Dbase db);
        bool DoMove(Direction direction, bool playerOp);
        bool TryMoveBlock(Block block, out Dbase db);      
    }

    // 游戏数据
    public abstract class GameData<T>: IGameData where T : Block 
    {
        public int SrandSeed;
        public SRandom SRandom;
        public int Width;
        public int Height;
        public List<List<T>> Blocks = new List<List<T>>(); // 所有可移动色块
        public T EmptyBlock;

        public abstract bool GameFinish { get; }
        public abstract bool BlockMatch(int x, int y);

        public virtual IGameData Init(int w, int h, int c, int srandSeed)
        {
            SrandSeed = srandSeed;            
            SRandom = new SRandom(srandSeed);
            Width = w;
            Height = h;

            return this;
        }

        /// <summary>
        /// 向随机一个可能的方向移动
        /// </summary>
        public void RandomMove(SRandom srand, int times)
        {
            int dir = -1;
            var moveChecks = new Func<bool>[] { CanLeft, CanUp, CanDown, CanRight };

            for (int i = 0; i < times; i++)
            {
                var move = moveChecks.GetRandomOneWithFilter((m) => m.Invoke() && Array.IndexOf(moveChecks, m) + dir != (int)Direction.None - 1, srand);
                dir = moveChecks.IndexOf(move);
                DoMove((Direction)dir, false, out Dbase db);
            }
        }

        /// <summary>
        /// 当前状况所有可以采取的移动行为
        /// </summary>
        public List<Direction> DirectionsCanMove()
        {
            var l = new List<Direction>();
            var moveChecks = new Func<bool>[] { CanLeft, CanUp, CanDown, CanRight };

            for (int i = 0; i < moveChecks.Length; i++)
            {
                var check = moveChecks[i];
                if (!check()) continue;

                l.Add((Direction)i);
            }

            return l;
        }

        // 一次移动操作
        public delegate void MoveOp(bool playerOp);
        public delegate void GetNeighbor(out int x, out int y);

        /// <summary>
        /// 根据方向移动
        /// </summary>
        public bool DoMove(Direction direction, bool playerOp, out Dbase db)
        {
            GetNeighbor getNeighbor;
            Func<bool> check;
            db = null;

            switch (direction)
            {
                case Direction.Left:
                    getNeighbor = EmptyBlock.Left;
                    check = CanLeft;
                    break;
                case Direction.Up:
                    getNeighbor = EmptyBlock.Up;
                    check = CanUp;
                    break;
                case Direction.Down:
                    getNeighbor = EmptyBlock.Down;
                    check = CanDown;
                    break;
                case Direction.Right:
                    getNeighbor = EmptyBlock.Right;
                    check = CanRight;
                    break;
                case Direction.None:
                default:
                    return false;
            }

            if (!check())
                return false;

            var x1 = EmptyBlock.X;
            var y1 = EmptyBlock.Y;
            getNeighbor(out int x, out int y);
            SwapBlock(EmptyBlock.X, EmptyBlock.Y, x, y);
            db = new Dbase
            {
                ["X1"] = x1,
                ["Y1"] = y1,
                ["X2"] = x,
                ["Y2"] = y,
                ["D"] = (int)direction
            };

            return true;
        }

        public bool DoMove(Direction direction, bool playerOp)
        {
            return DoMove(direction, playerOp, out Dbase db);
        }

        // 方向检查条件
        private bool CanLeft()
        {
            return EmptyBlock.X > 0;
        }
        private bool CanRight()
        {
            return EmptyBlock.X < Width - 1;
        }
        private bool CanUp()
        {
            return EmptyBlock.Y > 0;
        }
        private bool CanDown()
        {
            return EmptyBlock.Y < Height - 1;
        }

        private void SwapBlock(int x1, int y1, int x2, int y2)
        {
            var b1 = Blocks[y1][x1];
            var b2 = Blocks[y2][x2];
            Blocks[y1][x1] = b2;
            b2.X = x1;
            b2.Y = y1;
            Blocks[y2][x2] = b1;
            b1.X = x2;
            b1.Y = y2;
        }

        // 尝试移动一个方块
        public bool TryMoveBlock(Block block, out Dbase db)
        {
            // 要求与空位置的距离是1
            var dir = EmptyBlock.GetDirection(block);
            return DoMove(dir, true, out db);
        }
    }
}
